﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.UI;
using OpenQuarters.WebQuarters.Core;
using OpenQuarters.WebQuarters.Utils;
using OpenQuarters.WebQuarters.Modules.ItemList;
using System.Xml.Serialization;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Mvc;

namespace OpenQuarters.WebQuarters.Modules.ItemSearch
{
    public class ItemSearchModule : Core.Module
    {
        private string itemType = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableItemTypes")]
        public string ItemType
        {
            get { return itemType; }
            set { itemType = value; }
        }

        public static Dictionary<string, string> QueryableListProviders
        {
            get
            {
                Dictionary<string, string> methods = new Dictionary<string, string>();

                List<MethodInfo> allMethods = GeneralUtils.GetMethodsWithAttribute<Core.QueryableListProviderAttribute, Core.QueryableListProviderContainerAttribute>();

                // Add default
                methods.Add("", "[Default]");

                foreach (MethodInfo method in allMethods)
                {
                    methods.Add(method.Module.Assembly.GetName().Name + "," + method.DeclaringType.Name + "," + method.Name, method.Name);
                }

                return methods;
            }
        }

        private string queryableListProvider = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.QueryableListProviders")]
        public string QueryableListProvider
        {
            get { return queryableListProvider; }
            set { queryableListProvider = value; }
        }

        private string resultsView = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableViews/[Form.ItemType]")]
        public string Results_View
        {
            get { return resultsView; }
            set { resultsView = value; }
        }

        private string resultsItemView = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableViews/[Form.ItemType]")]
        public string Results_Item_View
        {
            get { return resultsItemView; }
            set { resultsItemView = value; }
        }

        private string mainView = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableViews/[Form.ItemType]")]
        public string Main_View
        {
            get { return mainView; }
            set { mainView = value; }
        }

        private string pagerView = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemList.ItemListModule.AvailableViews/[Form.ItemType]")]
        public string Pager_View
        {
            get { return pagerView; }
            set { pagerView = value; }
        }

        private string fullViewPage = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.CMSPage)]
        public string Full_View_Page
        {
            get { return fullViewPage; }
            set { fullViewPage = value; }
        }

        [Newtonsoft.Json.JsonProperty]
        public List<string> OrderableFields
        {
            get
            {
                return Core.OrderableAttribute.GetOrderableFields(GeneralUtils.GetTypeByName(this.ItemType));
            }
        }

        [Newtonsoft.Json.JsonProperty]
        public List<string> FilterableFields
        {
            get
            {
                return Core.FilterableAttribute.GetFilterableFields(GeneralUtils.GetTypeByName(this.ItemType));
            }
        }

        private List<Filter> filters = new List<Filter>();
        [Newtonsoft.Json.JsonProperty]
        public List<Filter> Filters
        {
            get { return filters; }
            set { filters = value; }
        }

        private List<Ordering> ordering = new List<Ordering>();
        [Newtonsoft.Json.JsonProperty]
        public List<Ordering> Ordering
        {
            get { return ordering; }
            set { ordering = value; }
        }

        private bool maintainList = true;

        [PropertyConfiguration(PropertyType.CheckBox)]
        [Newtonsoft.Json.JsonProperty]
        public bool Maintain_List
        {
            get { return maintainList; }
            set { maintainList = value; }
        }

        private int itemsPerPage = 5;

        [PropertyConfiguration(PropertyType.TextBox)]
        [Newtonsoft.Json.JsonProperty]
        public int Items_Per_Page
        {
            get { return itemsPerPage; }
            set { itemsPerPage = value; }
        }

        private int currentPage = 1;

        public int Current_Page
        {
            get { return currentPage; }
            set { currentPage = value; }
        }

        private PaginationMode paginationMode = PaginationMode.Number_List;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Core.PaginationMode")]
        public PaginationMode Pagination_Mode
        {
            get { return paginationMode; }
            set { paginationMode = value; }
        }

        private PaginationPosition paginationPosition = PaginationPosition.Top;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Core.PaginationPosition")]
        public PaginationPosition Pagination_Position
        {
            get { return paginationPosition; }
            set { paginationPosition = value; }
        }

        public static Dictionary<string, string> AvailableForms
        {
            get
            {
                Dictionary<string, string> d = new Dictionary<string, string>();
                d.Add("", "[None]");

                foreach (string path in System.IO.Directory.GetFiles(Oracle.Request.MapPath("~/Forms/"), "*.tpl"))
                {
                    d.Add(System.IO.Path.GetFileNameWithoutExtension(path), System.IO.Path.GetFileName(path));
                }

                return d;
            }
        }

        private string search_Form = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.DropDown, OptionSource = "OpenQuarters.WebQuarters.Modules.ItemSearch.ItemSearchModule.AvailableForms")]
        public string Search_Form
        {
            get { return search_Form; }
            set { search_Form = value; }
        }

        private bool useJavaScript = true;

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.CheckBox)]
        public Boolean Use_Javascript
        {
            get { return useJavaScript; }
            set { useJavaScript = value; }
        }

        private string onLoadedScript = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.TextBox)]
        public string On_Loaded_Script
        {
            get { return onLoadedScript; }
            set { onLoadedScript = value; }
        }

        private string onProceedScript = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.TextBox)]
        public string On_Proceed_Script
        {
            get { return onProceedScript; }
            set { onProceedScript = value; }
        }

        private string onSuccessScript = "";

        [Newtonsoft.Json.JsonProperty]
        [PropertyConfiguration(PropertyType.TextBox)]
        public string On_Success_Script
        {
            get { return onSuccessScript; }
            set { onSuccessScript = value; }
        }

        public ItemSearchModule()
            : base()
        {
        }

        public override void Initialise()
        {
            this.ModuleName = "Item Search Module";
            this.ModuleDescription = "Provides a search form and results list of ";
        }

        public override string GetJQueryEditorName()
        {
            return "cmsModuleEditor, cmsModuleEditor_ItemList";
        }

        Control itemListerControl = null;
        ItemListerConfiguration config = new ItemListerConfiguration();

        public ItemListerConfiguration Config
        {
            get { return config; }
            set { config = value; }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (Core.User.Current.HasPermission(Permissions.CMSPage.Edit.SetValue(CMSPage.Current.PageId.ToString())))
            {
                // We are using the content module editor with a custom onSetup and onSave methods for inline editing of entity fields
                JavaScript.RegisterScript("Modules.Content.AdminScript.js", "OpenQuarters.WebQuarters.Modules.Core", "Resources.Content.AdminScript.js", JavaScriptType.AdminScript);

                JavaScript.RegisterScript("Modules.ItemList.ItemListEditor.js", "OpenQuarters.WebQuarters.Modules.ItemList", "Resources.Scripts.ItemListEditor.js", JavaScriptType.AdminScript);
                CSS.RegisterStyleSheet("Modules.ItemList.ItemListEditor.css", "OpenQuarters.WebQuarters.Modules.ItemList", "Resources.Css.ItemListEditor.css", CSSType.AdminStyleSheet);

                if (itemListerControl != null)
                {
                    var targetItem = itemListerControl.GetType().GetProperty("TargetItem").GetValue(itemListerControl, null);
                    this.Controls.AddAt(0, new LiteralControl(@"<textarea style=""display:none;"" class=""additionalConfig"">{ FullViewMode: " + (targetItem != null ? "true" : "false") + " }</textarea>"));
                }
            }
        }

        protected override void OnInit(EventArgs e)
        {
            if (Request.Headers["Accept"] != null && Request.Headers["Accept"].IndexOf("application/json") != -1)
            {
                // Do nothing if json request
            }
            else
            {
                base.OnInit(e);

                if (!string.IsNullOrEmpty(fullViewPage))
                {
                    // Handle postback
                    if (!string.IsNullOrEmpty(this.itemType))
                    {
                        config = new ItemListerConfiguration();

                        config = ProcessRequestVariables(Request, config);

                        // Configure results
                        config.SummaryView = this.resultsView;
                        config.SummaryItemView = this.resultsItemView;
                        config.MainView = this.mainView;
                        config.PaginationView = pagerView;
                        config.FullViewPage = fullViewPage;
                        config.MaintainList = this.maintainList;
                        config.ItemListerId = this.ModuleId;
                        config.Items_Per_Page = this.Items_Per_Page;
                        config.Pagination_Mode = this.Pagination_Mode;
                        config.Pagination_Position = this.Pagination_Position;
                        config.Current_Page = this.currentPage;
                        config.Method = this.QueryableListProvider;
                        config.Ordering.AddRange(this.ordering);
                        config.Filters.AddRange(this.filters);

                        //Get current page
                        if (Regex.IsMatch(Request.Url.AbsoluteUri.ToString(), @"/page-\d*(\/||$)"))
                        {
                            Match pageMatches = Regex.Match(Request.Url.AbsoluteUri.ToString(), @"/page-(\d*)(\/||$)");
                            config.Current_Page = Convert.ToInt32(pageMatches.Groups[1].Value);
                        }
                        else if (!string.IsNullOrEmpty(Request.Form["cms_CurrentPage"]))
                        {
                            try
                            {
                                config.Current_Page = Convert.ToInt16(Request.Form["cms_CurrentPage"]);
                            }
                            catch (FormatException) { }
                        }

                        //Get items per page
                        if (Regex.IsMatch(Request.Url.AbsoluteUri.ToString(), @"/all(\/||$)"))
                        {
                            config.Current_Page = 0;
                            config.Items_Per_Page = 0;
                        }
                        else if (!string.IsNullOrEmpty(Request.Form["cms_ItemsPerPage"]))
                        {
                            try
                            {
                                config.Items_Per_Page = Convert.ToInt16(Request.Form["cms_ItemsPerPage"]);

                                if (config.Items_Per_Page == 0)
                                    config.Current_Page = 0;
                            }
                            catch (FormatException) { }
                        }


                        itemListerControl = (Control)typeof(ItemListerControl<>).MakeGenericType(new Type[] { Utils.GeneralUtils.GetTypeByName(this.itemType) }).GetConstructor(new Type[] { typeof(ItemListerConfiguration) }).Invoke(new object[] { config });
                    }
                }
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            if (Request.Headers["Accept"] != null && Request.Headers["Accept"].IndexOf("application/json") != -1)
            {
                // Do nothing if json request
            }
            else
            {
                base.OnLoad(e);

                if (!string.IsNullOrEmpty(fullViewPage))
                {
                    CMSPage page = CMSPage.Load(Convert.ToInt32(fullViewPage));
                    // Create search form
                    if (!string.IsNullOrEmpty(search_Form))
                    {
                        if (search_Form.IndexOf(",") == -1 && System.IO.File.Exists(Server.MapPath("~/Forms/" + search_Form + ".tpl")))
                        {
                            if (this.useJavaScript)
                            {
                                JavaScript.RegisterScript("SearchHandler", "OpenQuarters.WebQuarters.Modules.ItemSearch", "Resources.SearchHandler.js", JavaScriptType.PageScript);

                                XmlSerializer Serializer = new XmlSerializer(typeof(ItemListerConfiguration));
                                StringWriter configStream = new StringWriter();
                                Serializer.Serialize(configStream, config);
                                Utils.Encryption.SimpleAES aes = new Encryption.SimpleAES();
                                string encyptedOptions = aes.EncryptToString(configStream.ToString());

                                if (page != null && Request.Url.AbsoluteUri.IndexOf("/site/" + page.CurrentVersion.CMSPage.Urls[0].PageUrl) != -1)
                                {
                                    // Perform on page search 
                                    this.Controls.Add(new JForm(search_Form, "/cms/modules/MethodHandler/OpenQuarters.WebQuarters.Modules.ItemSearch,ItemSearchModule,AjaxSearchHandler", new { cmsEnv_ItemType = this.ItemType, cmsEnv_options = encyptedOptions })
                                    {
                                        UseJavaScript = true,
                                        OnLoadedScript = (!string.IsNullOrEmpty(this.onLoadedScript) ? this.onLoadedScript : ""),
                                        OnSuccessScript = (!string.IsNullOrEmpty(this.onSuccessScript) ? this.onSuccessScript : "ItemSearchModule_HandleResults(response)"),
                                        OnProceedScript = (!string.IsNullOrEmpty(this.onProceedScript) ? this.onProceedScript : ""),
                                        Method = JFormControl<DefaultJFormClass>.MethodType.GET
                                    });
                                }
                                else
                                {
                                    // Search form not on results page so post to page search results page
                                    this.Controls.Add(new JForm(search_Form, (page == null ? "" : "/site/" + page.CurrentVersion.CMSPage.Urls[0].PageUrl))
                                    {
                                        UseJavaScript = false,
                                        Method = JFormControl<DefaultJFormClass>.MethodType.GET
                                    });
                                }
                            }
                            else
                            {
                                // Post to search results page (without js)
                                this.Controls.Add(new JForm(search_Form, (page == null ? "" : "/site/" + page.CurrentVersion.CMSPage.Urls[0].PageUrl))
                                {
                                    UseJavaScript = false,
                                    Method = JFormControl<DefaultJFormClass>.MethodType.GET
                                });
                            }
                        }
                        else
                        {
                            this.Controls.Add(new JForm(search_Form, JForm.TemplateType.EmbeddedResource, "/site/" + page.CurrentVersion.CMSPage.Urls[0].PageUrl)
                            {
                                UseJavaScript = this.useJavaScript,
                                Method = JFormControl<DefaultJFormClass>.MethodType.GET
                            });
                        }
                    }

                    this.Controls.Add(itemListerControl);
                }
            }
        }

        public static JFormResponse<ItemListResponse> AjaxSearchHandler(HttpRequest request)
        {
            string itemType = request.Form["cmsEnv_ItemType"];
            var resp = new JFormResponse<ItemListResponse>();
            ItemListResponse results = new ItemListResponse();
            object itemLister;

            Utils.Encryption.SimpleAES aes = new Encryption.SimpleAES();
            XmlSerializer Serializer = new XmlSerializer(typeof(ItemListerConfiguration));
            ItemListerConfiguration config = (ItemListerConfiguration)Serializer.Deserialize(new StringReader(aes.DecryptString(request.Form["cmsEnv_options"])));

            config = ProcessRequestVariables(request, config);

            //Get current page
            if (Regex.IsMatch(request.Url.AbsoluteUri.ToString(), @"/page-\d*(\/||$)"))
            {
                Match pageMatches = Regex.Match(request.Url.AbsoluteUri.ToString(), @"/page-(\d*)(\/||$)");
                config.Current_Page = Convert.ToInt32(pageMatches.Groups[1].Value);
            }
            else if (!string.IsNullOrEmpty(request.Form["cms_CurrentPage"]))
            {
                try
                {
                    config.Current_Page = Convert.ToInt16(request.Form["cms_CurrentPage"]);
                }
                catch (FormatException) { }
            }

            //Get items per page
            if (Regex.IsMatch(request.Url.AbsoluteUri.ToString(), @"/all(\/||$)"))
            {
                config.Current_Page = 0;
                config.Items_Per_Page = 0;
            }
            else if (!string.IsNullOrEmpty(request.Form["cms_ItemsPerPage"]))
            {
                try
                {
                    config.Items_Per_Page = Convert.ToInt16(request.Form["cms_ItemsPerPage"]);

                    if (config.Items_Per_Page == 0)
                        config.Current_Page = 0;
                }
                catch (FormatException) { }
            }

            //itemLister = typeof(ItemLister<>).MakeGenericType(new Type[] { Utils.GeneralUtils.GetTypeByName(itemType) }).GetConstructor(new Type[] { typeof(ItemListerConfiguration) }).Invoke(new object[] { config, Utils.GeneralUtils.GetTypeByName(itemType) });
            itemLister = typeof(ItemLister<>).MakeGenericType(new Type[] { Utils.GeneralUtils.GetTypeByName(itemType) }).GetConstructor(new Type[] { }).Invoke(new object[] { });
            //var targetItem = itemListerControl.GetType().GetProperty("TargetItem").GetValue(itemListerControl, null);
            itemLister.GetType().GetProperty("Configuration").SetValue(itemLister, config, new object[] { });
            //itemLister.GetType().GetProperty("ItemType").SetValue(itemLister, Utils.GeneralUtils.GetTypeByName(itemType), new object[] { });
            results = (ItemListResponse)typeof(ItemLister<>).MakeGenericType(new Type[] { Utils.GeneralUtils.GetTypeByName(itemType) }).GetMethod("BuildItemList").Invoke(itemLister, null);
            results.ItemType = itemType;
            results.Configuration = config;

            //clear filters and ordering
            config.Filters.Clear();
            config.Ordering.Clear();

            resp.result = results;

            JFormResponse<ItemListResponse>.Current = resp;

            return resp;
        }

        /// <summary>
        /// Processes the posted form (get/post) and parses it into config object
        /// </summary>
        /// <param name="request">The current HTTP request</param>
        /// <param name="config">The config class to populate and return</param>
        /// <returns>The original config variable with the additional parsed options</returns>
        public static ItemListerConfiguration ProcessRequestVariables(HttpRequest request, ItemListerConfiguration config)
        {
            
            List<string> keys = new List<string>();
            keys.AddRange(Oracle.Request.QueryString.Keys.Cast<string>());
            keys.AddRange(Oracle.Request.Form.Keys.Cast<string>());

            foreach (string key in keys)
            {
                if (key.StartsWith("cmsEnv_filter") && !string.IsNullOrEmpty(Oracle.Request[key]))
                {
                    string[] filterArgs = key.Split('_');

                    Filter filter = new Filter();
                    filter.Field = key.Substring(filterArgs[0].Length + filterArgs[1].Length + 2);

                    // parse filter
                    switch (filterArgs[1].ToLower())
                    {
                        case "filterequals":
                            filter.Operator = "=";
                            break;
                        case "filterdoesnotequal":
                            filter.Operator = "!=";
                            break;
                        case "filterlessthan":
                            filter.Operator = "<";
                            break;
                        case "filterlessthanorequalto":
                            filter.Operator = "<=";
                            break;
                        case "filtermorethan":
                            filter.Operator = ">";
                            break;
                        case "filtermorethanorequalto":
                            filter.Operator = ">=";
                            break;
                        case "filterin":
                            filter.Operator = "IN";
                            break;
                        case "filterdoesnotcontain":
                            filter.Operator = "!%";
                            break;
                        case "filteristrue":
                            try
                            {
                                if (bool.Parse(Oracle.Request[key]))
                                    filter.Operator = "=";
                                else
                                    filter.Operator = "";
                            }
                            catch (FormatException) { filter.Operator = ""; }
                            break;
                        case "filterisfalse":
                            try
                            {
                                if (bool.Parse(Oracle.Request[key]))
                                    filter.Operator = "!=";
                                else
                                    filter.Operator = "";
                            }
                            catch (FormatException) { filter.Operator = ""; }
                            break;
                        default:
                            filter.Operator = "%"; //Contains
                            break;
                    }

                    if (filter.Operator.Length != 0)
                    {
                        filter.Value = Oracle.Request[key];
                        if (config.Filters == null)
                            config.Filters = new List<Filter>();
                        config.Filters.Add(filter);
                    }
                }

                if (key.StartsWith("cmsEnv_order") && !string.IsNullOrEmpty(Oracle.Request[key]))
                {
                    foreach (string field in Oracle.Request[key].Split(','))
                    {
                        string[] orderArgs = field.Trim().Split(' ');

                        Ordering order = new Ordering();
                        order.Field = orderArgs[0];

                        if (orderArgs[orderArgs.Length - 1].ToLower() == "asc" || orderArgs[orderArgs.Length - 1].ToLower() == "desc")
                            order.Operator = orderArgs[orderArgs.Length - 1].ToLower();
                        else
                            order.Operator = "asc";

                        if (config.Ordering == null)
                            config.Ordering = new List<Ordering>();

                        config.Ordering.Add(order);
                    }
                }
            }

            return config;
        }

        [Invokable]
        public static object AjaxRenderResults(HttpRequestWrapper request)
        {
            Type targetType = GeneralUtils.GetTypeByName(request["itemType"]);

            List<object> items = new List<object>();

            foreach (string item in request["jsonItems"].Replace("[", "").Replace("]", "").Replace("},", "}").Split('}'))
            {
                if (!string.IsNullOrEmpty(item))
                {
                    string serializedItem = item + "}";

                    object obj = typeof(GeneralUtils).GetMethod("FromJSON", new Type[] { typeof(string) }).MakeGenericMethod(new Type[] { targetType }).Invoke(null, new object[] { serializedItem });
                    items.Add(obj);
                }
            }

            MethodInfo CastMethod = typeof(Enumerable).GetMethod("Cast");
            MethodInfo ToListMethod = typeof(Enumerable).GetMethod("ToList");

            var castItems = CastMethod.MakeGenericMethod(new Type[] { targetType })
                          .Invoke(null, new object[] { items });
            var list = ToListMethod.MakeGenericMethod(new Type[] { targetType })
                          .Invoke(null, new object[] { castItems });

            return list;
        }

        [Invokable]
        public static object AjaxRenderResultsItem(HttpRequestWrapper request)
        {
            Type targetType = GeneralUtils.GetTypeByName(request["itemType"]);

            string item = request["jsonItem"];

            if (!string.IsNullOrEmpty(item))
            {
                string serializedItem = item; // +"}";

                object obj = typeof(GeneralUtils).GetMethod("FromJSON", new Type[] { typeof(string) }).MakeGenericMethod(new Type[] { targetType }).Invoke(null, new object[] { serializedItem });

                return obj;
            }

            return null;
        }


        [Invokable]
        public static object AjaxRenderDetailItem(HttpRequestWrapper request)
        {
            Type targetType = GeneralUtils.GetTypeByName(request["itemType"]);

            string itemId = request["jsonItemId"];

            object item = targetType.GetConstructor(new Type[] { }).Invoke(new object[] { });

            object obj = targetType.GetMethod("LoadItem", new Type[] { typeof(string) }).Invoke(item, new object[] { itemId });

            return obj;
        }

        [Invokable]
        public static object AjaxRenderPager(HttpRequestWrapper request)
        {
            PaginationData paginationData = GeneralUtils.FromJSON<PaginationData>(request["jsonPaginationData"]);

            return paginationData;
        }

    }
}
